home *** CD-ROM | disk | FTP | other *** search
-
- #include <Types.h>
- #include <SysEqu.h>
- #include <Resources.h>
- #include <QuickDraw.h>
- #include <Fonts.h>
- #include <Events.h>
- #include <Menus.h>
- #include <Controls.h>
- #include <Dialogs.h>
- #include <Memory.h>
- #include <Files.h>
- #include <Devices.h>
- #include <Serial.h>
- #include <Timer.h>
- #include <OSUtils.h>
- #include <GestaltEqu.h>
- #include <Errors.h>
- #include <string.h>
-
-
-
- #define rMonitor 1000
- #define kSendButton 1
- #define kStopButton 2
- #define kQuitButton 3
- #define kMsgBox 4
- #define kSendSpinner 5
- #define kRcvSpinner 7
- #define kHoldFlag 9
- #define kBreakButton 10
-
- #define rSerDataRsrc 128
- #define rSpinnerIcon 1000
- #define rHeldIcon 128
- #define rNotHeldIcon 129
-
- #define rPortOpenALRT 256
- #define kReset 2
-
- #define kCtlEnable 0
- #define kCtlDisable 255
- #define kSerBufSize 16384
- #define kSerRdSize 64
- #define kSerConfig baud9600 + noParity + data8 + stop10
- #define kBreakLength 666 /* serial break length in milliseconds */
-
- enum {drvrName = 0x12}; /* offset to driver name in 'DRVR' std. header */
- enum {dOpened = 5,
- dRAMBased = 6,
- drvrActive = 7}; /* Device Manager DCtlFlag bits */
-
- enum {kSerStatus = 8,
- kSerClrBrk = 11, /* Serial Driver csCodes */
- kSerSetBrk = 12,
- kSerHShakeDTR = 14};
- enum {breakR0 = 128}; /* mask for break bit in SCC RR0 -- See TN #56 */
- enum {breakErr = 8}; /* mask for break bit in cumErrs -- System 7.0 */
-
-
-
- typedef struct {
- IOParam fIOParam;
- long appA5;
- } ExtIOParam;
-
- typedef struct {
- TMTask fTMTask;
- long appA5;
- } ExtTMTask;
-
- typedef struct {
- unsigned char readR0;
- unsigned char deltaBits;
- short drvrPosting;
- } SERDEventMessage;
-
-
-
- Boolean gHeldOff,
- gAllDone = false,
- gShouldSend = false,
- gReload = false,
- gKillBreak = false,
- gBreakReceived = false;
- Ptr gpSerBuf, *ghSerBuf,
- gpOutputData, *ghOutputData,
- gpBitBucket, *ghBitBucket;
- long gOutputDataSize;
- IOParam *gpSendPB, **ghSendPB;
- short gSysVersion,
- gOutRefNum, gInRefNum,
- gSendCount = 0,
- gRcvCount = 0;
- char *panicString = "Help! I'm stuffed! And here's a bunch of characters to prove it!\n\r";
- ExtTMTask gUnBreakTask;
-
-
-
- short Initialize (void);
- void CleanUp (void);
- Boolean OpenSERD (void);
- void CloseSERD (void);
- void DoIOStuff (void);
- OSErr PrimeSend (void);
- void CheckSerStatus (void);
- void CheckSerData (long reqBytes);
- pascal Boolean NullGrabber (DialogPtr, EventRecord *evt, short *itemHit);
- pascal void SendCompRout (void);
- pascal void FlagBreakTimeout (void);
- OSErr AssertDrvrOpen (Str255 name, short *refNum);
-
- ParmBlkPtr GetParmBlkPtr (void) = 0x2008; /* MOVE.L A0,D0 */
- TMTaskPtr GetTMTaskPtr (void) = 0x2009; /* MOVE.L A1,D0 */
-
- #pragma parameter __D0 PBControlImmed(__A0)
- pascal OSErr PBControlImmed(ParmBlkPtr paramBlock) = 0xA204; /* _Control ,IMMED */
-
- #pragma parameter __D0 PBStatusImmed(__A0)
- pascal OSErr PBStatusImmed(ParmBlkPtr paramBlock) = 0xA205; /* _Status ,IMMED */
-
-
-
- void main (void)
- {
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- InitDialogs(NULL);
- InitCursor();
-
- if (Initialize() == noErr) {
-
- if (OpenSERD()) { /* open the Serial Driver */
- DoIOStuff();
- CloseSERD(); /* close the Serial Driver */
- }
- CleanUp();
- }
-
- }
-
-
-
- Boolean OpenSERD (void)
- {
- OSErr openOutErr, openInErr;
- OSErr setBufErr, setCfgErr, setHskErr;
- SerShk hskFlags;
- long finalTicks;
- Boolean takeOverPort = true;
- Boolean openAOut, openAIn;
-
- openAOut = AssertDrvrOpen("\p.AOut", &gOutRefNum) == noErr;
- openAIn = AssertDrvrOpen("\p.AIn", &gInRefNum) == noErr;
- if (openAOut || openAIn) {
- if (takeOverPort = CautionAlert(rPortOpenALRT, nil) == kReset) {
- if (openAIn) {
- KillIO(gInRefNum);
- CloseDriver(gInRefNum);
- }
- if (openAOut) {
- KillIO(gOutRefNum);
- CloseDriver(gOutRefNum);
- }
- }
- }
-
- if (takeOverPort) {
- openOutErr = OpenDriver("\p.AOut", &gOutRefNum);
- openInErr = OpenDriver("\p.AIn", &gInRefNum);
-
- if (openOutErr == noErr && openInErr == noErr) {
-
- /* There is a bug in the Macintosh IIfx IOP serial driver, in which */
- /* SerGetBuf() will always return zero characters, even if characters */
- /* have been received. The bug is exhibited when a remote serial device */
- /* attempts to send data to the Macintosh IIfx when its serial driver is */
- /* not yet open and it holding off data with hardware handshaking. In */
- /* such a case, data will flow in immediately when the serial driver */
- /* opens--quickly filling up the default 64-byte buffer. If the buffer */
- /* fills up before setting a larger buffer with SerSetBuf(), SerGetBuf() */
- /* "sticks" and stubbornly maintains that there is nothing coming in. */
- /* At 9600 baud, it takes only a little more than four ticks to fill the */
- /* input buffer. This application demonstrates the bug by virtue of the */
- /* following delay. */
-
- Delay(5, &finalTicks);
-
- /* It's always good to first set a non-default input buffer, if desired. */
- /* There is no output buffering, so specify only the input driver. */
-
- setBufErr = SerSetBuf(gInRefNum, gpSerBuf, kSerBufSize);
-
- hskFlags.fXOn = false;
- hskFlags.fCTS = true;
- hskFlags.xOn = 0x11;
- hskFlags.xOff = 0x13;
- hskFlags.errs = 0;
-
- if (gSysVersion >= 0x0700) {
- hskFlags.evts = 0; /* I can use new means of break detection. */
- }
- else {
- hskFlags.evts = breakEvent; /* I need the driver to post break events. */
- }
-
- hskFlags.fInX = false;
- hskFlags.fDTR = true;
-
- /* SerHShake() does not support full DTR/CTS hardware handshaking. You */
- /* accomplish the same thing and more with a Control call and csCode 14. */
- /* You only need to specify hskFlags once, to the output driver. */
-
- setHskErr = Control(gOutRefNum, kSerHShakeDTR, (Ptr) &hskFlags);
-
- /* Now reset both input and output drivers with the same configuration. */
- /* Only a single call to the output driver is necessary to do this. */
- /* Differing concurrent input/output baud rates are not supported. */
-
- setCfgErr = SerReset(gOutRefNum, kSerConfig);
-
- }
- }
-
- return takeOverPort;
- }
-
-
-
- void CloseSERD (void)
- {
- OSErr killErr, closeOutErr, closeInErr;
-
- killErr = KillIO(gInRefNum);
- closeInErr = CloseDriver(gInRefNum);
-
- killErr = KillIO(gOutRefNum);
- closeOutErr = CloseDriver(gOutRefNum);
-
- }
-
-
-
- void CheckSerStatus (void)
- {
- OSErr checkStatErr, panicErr;
- IOParam altSendPB, *pAltSendPB = &altSendPB;
- CntrlParam statPB;
- SerStaRec serStat;
-
- statPB.ioCRefNum = gInRefNum;
- statPB.csCode = kSerStatus;
- checkStatErr = PBStatusImmed((ParmBlkPtr) &statPB);
- serStat = *(SerStaRec *) &statPB.csParam;
-
- /* I check to see if the remote system has told me to stop sending. */
-
- gHeldOff = (Boolean) serStat.ctsHold;
-
- /* Check for errors. */
-
- if (serStat.cumErrs & swOverrunErr)
- DebugStr("\pSoftware Overrun Error");
- else if (serStat.cumErrs & parityErr)
- DebugStr("\pParity Error");
- else if (serStat.cumErrs & hwOverrunErr)
- DebugStr("\pHardware Overrun Error");
- else if (serStat.cumErrs & framingErr)
- DebugStr("\pFraming Error");
-
- /* If I have System 7.0 or better, I can check directly to see if */
- /* I've received a break. Usually I don't check for a feature this */
- /* way, but in this case I have no alternative. */
-
- if (gSysVersion >= 0x0700) {
- gBreakReceived = (serStat.cumErrs & breakErr) != 0;
- }
-
- /* All I do here is send a small "panic" packet of characters back */
- /* to the remote system when it fills _my_ buffer. I don't actually */
- /* know the exact state of my buffer, but I can see if I've told */
- /* the remote system to shut up, indicating that I'm mostly full. */
-
- if (checkStatErr == noErr) {
- if (( (unsigned char) serStat.xOffSent & dtrNegated) != 0) {
- pAltSendPB->ioCompletion = NULL;
- pAltSendPB->ioRefNum = gOutRefNum;
- pAltSendPB->ioBuffer = panicString;
- pAltSendPB->ioReqCount = strlen(panicString);
- PBWriteAsync((ParmBlkPtr) pAltSendPB);
-
- /* The program may hang here if the user quits the */
- /* remote application first--that could hold off our */
- /* serial output, leaving a pending asynchronous I/O */
- /* request and keeping us in an infinite loop. */
-
- while (pAltSendPB->ioResult > 0) {} /* I'll fix it later. */
- /* The reason I do this instead of just calling it synchronously */
- /* is so that if I do hang, I'll hang in my code for an obvious */
- /* reason instead of hanging up in the Device Manager. */
- panicErr = pAltSendPB->ioResult;
- }
- }
- }
-
-
-
- void CheckSerData (long reqBytes)
- {
- OSErr checkBufErr, serRdErr;
- long charCount;
-
- // long finalTicks;
- // register long overrun;
-
- checkBufErr = SerGetBuf(gInRefNum, &charCount);
- if (checkBufErr == noErr) {
-
- /* The general strategy here is this: if number of available characters */
- /* meets a certain minimum threshold, then I read in everything in the */
- /* buffer. If I get delayed, I'll catch up quickly. */
-
- if (charCount != 0 && charCount >= reqBytes) {
- /*
- overrun = charCount;
- Control(gOutRefNum, 18, nil);
- Delay(300, &finalTicks);
- Control(gOutRefNum, 17, nil);
- SerGetBuf(gInRefNum, &charCount);
- overrun = charCount - overrun;
- DebugStr("\pSender Halted");
- */
- serRdErr = FSRead(gInRefNum, &charCount, gpBitBucket);
- if (serRdErr == noErr) {
- gRcvCount++; /* increment a counter for the input spinner */
- }
- }
- }
- }
-
-
-
- void DoIOStuff (void)
- {
- DialogPtr serMonitor;
- OSErr primeErr;
- short itemHit, itemType;
- ControlHandle sendItem, stopItem, breakItem;
- Handle spinner, flag, item;
- Rect box;
- CntrlParam breakPB;
-
- serMonitor = GetNewDialog(rMonitor, NULL, NULL);
- SetPort((GrafPtr) serMonitor);
-
- while (!gAllDone) {
-
- CheckSerStatus();
-
- /* Holding down the mouse button prevents checking the input buffer */
- /* and forces the input buffer to fill up. This allows experimentation. */
-
- if (!Button()) {
- CheckSerData(kSerRdSize);
- }
-
- if (gShouldSend && gReload) {
- gSendCount++; /* increment a counter for the output spinner */
- gReload = !gReload;
- }
-
- /* The break timer simply sets a global flag which I use to indicate when */
- /* to clear a break condition. Again, I use an immediate Control call, but */
- /* primarily for consistency, and also to show off. */
-
- if (gKillBreak) {
- breakPB.ioCRefNum = gOutRefNum;
- breakPB.csCode = kSerClrBrk;
- PBControlImmed((ParmBlkPtr) &breakPB); /* SerClrBrk(), but IMMED */
- gKillBreak = !gKillBreak;
- GetDItem(serMonitor, kBreakButton, &itemType, &(Handle) breakItem, &box);
- HiliteControl(breakItem, kCtlEnable);
- }
-
- /* If another area of the program detects a break, I flag the occurrence here. */
-
- if (gBreakReceived) {
- SysBeep(1);
- SysBeep(1);
- gBreakReceived = !gBreakReceived;
- }
-
- /* Here I update all the buttons and icons. I probably spend too much time */
- /* doing this when it isn't necessary, but that's not the point. */
-
- /* I animate the beach balls by changing the resource IDs of the ICONs in the */
- /* DITL and invalidating their enclosing rectangles. I also hilight controls */
- /* appropriately and display annunciators when necessary. */
-
- GetDItem(serMonitor, kSendButton, &itemType, &(Handle) sendItem, &box);
- GetDItem(serMonitor, kStopButton, &itemType, &(Handle) stopItem, &box);
- if (gShouldSend) {
- HiliteControl(sendItem, kCtlDisable);
- HiliteControl(stopItem, kCtlEnable);
- }
- else {
- HiliteControl(sendItem, kCtlEnable);
- HiliteControl(stopItem, kCtlDisable);
- }
-
- spinner = Get1Resource('ICON', rSpinnerIcon + gSendCount % 8);
- GetDItem(serMonitor, kSendSpinner, &itemType, &item, &box);
- SetDItem(serMonitor, kSendSpinner, iconItem, spinner, &box);
- InvalRect(&box);
-
- spinner = Get1Resource('ICON', rSpinnerIcon + gRcvCount % 8);
- GetDItem(serMonitor, kRcvSpinner, &itemType, &item, &box);
- SetDItem(serMonitor, kRcvSpinner, iconItem, spinner, &box);
- InvalRect(&box);
-
- if (gShouldSend && gHeldOff) {
- flag = Get1Resource('ICON', rHeldIcon);
- }
- else {
- flag = Get1Resource('ICON', rNotHeldIcon);
- }
- GetDItem(serMonitor, kHoldFlag, &itemType, &item, &box);
- SetDItem(serMonitor, kHoldFlag, iconItem, flag, &box);
- InvalRect(&box);
-
- /* In lieu of an event loop, I just use a modal dialog with a relatively */
- /* simple (but unusual) filterProc. This is not a good example of how to */
- /* write an app. Modal dialogs are evil and to be avoided if possible. */
- /* Nonetheless, the filterProc is an interesting example unto itself.... */
-
- ModalDialog(NullGrabber, &itemHit);
- switch (itemHit) {
-
- case kStopButton:
- if (gShouldSend) {
- gShouldSend = !gShouldSend;
- }
- break;
-
- case kSendButton:
- if (!gShouldSend) {
- gShouldSend = !gShouldSend;
- primeErr = PrimeSend();
- }
- break;
-
- case kBreakButton:
-
- /* In another possible Mac IIfx IOP bug, SerSetBrk() called synchronously */
- /* appears to hang the machine if an async write is pending. Since that is */
- /* often the case (at least in this application) I work around the problem */
- /* by making the Control call immediate--this prevents the hang, but also */
- /* raises another interesting issue about when break is actually asserted. */
-
- breakPB.ioCRefNum = gOutRefNum;
- breakPB.csCode = kSerSetBrk;
- PBControlImmed((ParmBlkPtr) &breakPB); /* SerSetBrk(), but IMMED */
-
- /* With break asserted, I prime a Time Manager task to flag the end of */
- /* the break, and disable the Break button so that it cannot be selected */
- /* again until break is negated. */
-
- PrimeTime((QElemPtr) &gUnBreakTask, kBreakLength);
- GetDItem(serMonitor, kBreakButton, &itemType, &(Handle) breakItem, &box);
- HiliteControl(breakItem, kCtlDisable);
- break;
-
- case kQuitButton:
- gAllDone = true;
- break;
-
- }
- }
-
- DisposDialog(serMonitor);
-
- }
-
-
-
- pascal Boolean NullGrabber (DialogPtr, EventRecord *evt, short *itemHit)
- {
- EventRecord driverEvent;
-
- /* Without this filterProc, none of the animation works: */
-
- /* In order to keep things rolling along even when there are no events */
- /* such as mouse clicks or keystrokes, I have to return true in response */
- /* to null events. This is unusual, but otherwise ModalDialog() "handles" */
- /* null events by eating them and waiting for something better. This is */
- /* bad if I need to turn the spinner or clear a break condition. */
-
- /* Also, I check for driver events here in order to detect breaks. */
-
- if (GetNextEvent(driverMask, &driverEvent)) {
- if ((*(SERDEventMessage *) &driverEvent.message).drvrPosting == gInRefNum) {
- gBreakReceived = ((*(SERDEventMessage *) &driverEvent.message).readR0 & breakR0) != 0;
- }
- }
-
- if (evt->what == nullEvent) {
- *itemHit = 0;
- return true;
- }
-
- else {
- return false;
- }
-
- }
-
-
-
- pascal void SendCompRout (void)
- {
- long oldA5;
-
- oldA5 = SetA5(((ExtIOParam *) GetParmBlkPtr())->appA5); /* retrieve A5 */
-
- if (gShouldSend && !gAllDone) {
- gReload = true;
- gpSendPB->ioCompletion = (ProcPtr) SendCompRout;
- PBWriteAsync((ParmBlkPtr) gpSendPB); /* this is the self-sustaining part */
- }
-
- SetA5(oldA5);
-
- }
-
-
-
- OSErr PrimeSend (void)
- {
- gpSendPB->ioCompletion = (ProcPtr) SendCompRout;
- gpSendPB->ioRefNum = gOutRefNum;
- gpSendPB->ioBuffer = gpOutputData;
- gpSendPB->ioReqCount = gOutputDataSize;
- ((ExtIOParam *) gpSendPB)->appA5 = SetCurrentA5(); /* completion routine needs A5 */
- gReload = true;
-
- return PBWriteAsync((ParmBlkPtr) gpSendPB); /* asynchronous self-sustaining sends */
-
- }
-
-
-
- pascal void FlagBreakTimeout (void)
- {
- long oldA5;
-
- oldA5 = SetA5(((ExtTMTask *) GetTMTaskPtr())->appA5); /* retrieve A5 */
-
- gKillBreak = true;
-
- SetA5(oldA5);
-
- }
-
-
-
- OSErr AssertDrvrOpen (Str255 name, short *refNum)
- {
- DCtlHandle *pUTEntry;
- Ptr pDrvr;
- OSErr result = notOpenErr; /* assume not open */
- short unitNo;
- char *aDrvrName;
-
- /* The point here is to determine whether a driver is open, given its name. */
- /* This allows one to check a driver to see if it's open without hard coding */
- /* its reference number. (Normally, the way to get the refNum is to open */
- /* the driver--but that defeats the whole purpose!) */
- /* This is an extension of the code discussed in Tech Note #71. */
-
- *refNum = 0;
- pUTEntry = *(DCtlHandle **) UTableBase;
- for (unitNo = 0; unitNo < *(short *) UnitNtryCnt; unitNo++, pUTEntry++) {
- if (*pUTEntry != nil && **pUTEntry != nil) {
- if (((***pUTEntry).dCtlFlags & 1 << dRAMBased) != 0)
- pDrvr = *(Handle) (***pUTEntry).dCtlDriver;
- else
- pDrvr = (***pUTEntry).dCtlDriver;
-
- if (pDrvr != nil) {
- aDrvrName = pDrvr + drvrName;
- if (memcmp(aDrvrName, name, 1 + name[0]) == 0) {
- /* We found the one we're after. */
- *refNum = ~unitNo;
- if (((***pUTEntry).dCtlFlags & 1 << dOpened) != 0)
- result = noErr;
- break;
- }
- }
- }
- }
-
- return result;
- }
-
-
-
- short Initialize (void)
- {
- long result;
- OSErr gestErr;
-
- gestErr = Gestalt(gestaltSystemVersion, &result);
- gSysVersion = result;
-
- ghSendPB = (IOParam **) NewHandle( sizeof(ExtIOParam));
- if (ghSendPB != NULL) {
- MoveHHi((Handle) ghSendPB);
- HLock((Handle) ghSendPB);
- gpSendPB = *ghSendPB;
- }
- else {
- return MemError();
- }
-
- ghSerBuf = NewHandle(kSerBufSize);
- if (ghSerBuf != NULL) {
- MoveHHi((Handle) ghSerBuf);
- HLock((Handle) ghSerBuf);
- gpSerBuf = *ghSerBuf;
- }
- else {
- DisposHandle((Handle) ghSendPB);
- return MemError();
- }
-
- ghBitBucket = NewHandle(kSerBufSize);
- if (ghBitBucket != NULL) {
- MoveHHi(ghBitBucket);
- HLock(ghBitBucket);
- gpBitBucket = *ghBitBucket;
- }
- else {
- DisposHandle((Handle) ghSendPB);
- DisposHandle((Handle) ghSerBuf);
- return MemError();
- }
-
- ghOutputData = Get1Resource('sDAT', rSerDataRsrc);
- if (ghOutputData != NULL) {
- MoveHHi(ghOutputData);
- HLock(ghOutputData);
- gpOutputData = *ghOutputData;
- gOutputDataSize = SizeResource(ghOutputData);
- }
- else {
- DisposHandle((Handle) ghSendPB);
- DisposHandle((Handle) ghSerBuf);
- DisposHandle(ghBitBucket);
- return ResError();
- }
-
- /* I use a Time Manager task to time breaks. This is where I install it. */
-
- gUnBreakTask.fTMTask.tmAddr = FlagBreakTimeout;
- gUnBreakTask.appA5 = SetCurrentA5();
- InsTime((QElemPtr) &gUnBreakTask);
-
- return noErr;
- }
-
-
-
- void CleanUp (void)
- {
- /* I remove the Time Manager task before exiting. */
-
- RmvTime((QElemPtr) &gUnBreakTask);
-
- DisposHandle((Handle) ghSendPB);
- DisposHandle(ghSerBuf);
- DisposHandle(ghBitBucket);
- }
-